#include "../device/8254.h"
#include "../device/8259A.h"
#include "../device/KBController.h"
#include "../filesystem/file.h"
#include "../kernel/interrupt.h"
#include "../kernel/memory.h"
#include "../kernel/process.h"
#include "../kernel/syscall.h"
#include "../lib/define.h"
#include "../lib/heap.h"
#include "../lib/rtl.h"

STATIC BYTE bFirstProc[0x600];

STATIC BYTE GDT[0x1000];

int __main(void);
void UserModeEntry(void);

/*
 * 0 ~ 3GB user space
 * 3GB+
 * 0 ~ 1MB only used for display
 * 1MB ~ 1MB128KB memory bitmap
 * 1MB128KB ~ 8MB execute and stack
 * 8MB ~ 32MB heap
 * 32MB ~ 40MB reserved
 *
 *
 */

int __main(void) {
  Dev8259AFirstInitialize(
      DEV_8259A_INIT_MAGIC_BIT | DEV_8259A_INIT_EDGE_TRIGGERED_MODE |
      DEV_8259A_INIT_VECTOR_8 | DEV_8259A_INIT_CASCADE_MODE |
      DEV_8259A_INIT_NEED_ICW4 | DEV_8259A_INIT_INT_VECTOR_BASE(0x20) |
      DEV_8259A_INIT_SLAVE(0x04) | DEV_8259A_INIT_NO_SPECIAL_FULLY_NESTED_MODE |
      DEV_8259A_INIT_NONBUFFERED_MODE | DEV_8259A_INIT_NORMAL_EOI |
      DEV_8259A_INIT_8086_8088_MODE);
  Dev8259ASecondInitialize(
      DEV_8259A_INIT_MAGIC_BIT | DEV_8259A_INIT_EDGE_TRIGGERED_MODE |
      DEV_8259A_INIT_VECTOR_8 | DEV_8259A_INIT_CASCADE_MODE |
      DEV_8259A_INIT_NEED_ICW4 | DEV_8259A_INIT_INT_VECTOR_BASE(0x28) |
      DEV_8259A_INIT_SLAVE_ATTACH(2) |
      DEV_8259A_INIT_NO_SPECIAL_FULLY_NESTED_MODE |
      DEV_8259A_INIT_NONBUFFERED_MODE | DEV_8259A_INIT_NORMAL_EOI |
      DEV_8259A_INIT_8086_8088_MODE);
  Dev8254SetModeAndCounterValue(DEV_8254_MODE_COUNTER0 |
                                    DEV_8254_MODE_SQUARE_WAVE_GENERATOR |
                                    DEV_8254_MODE_BINARY,
                                11932);
  Dev8259AFirstSetInterruptMask(0xF8);
  Dev8259ASecondSetInterruptMask(0xFF);
  DevKBControllerSendCommandSingle(DEV_KBCONTROLLER_COMMAND_ENABLE_MOUSE);
  IdtInitialize();
  SyscallInitialize();
  MmInitializeBitmap();
  FsInitialize();
  HeapInitialize((LPVOID)0xc0800000, 0x1800000);
  ThInitialize();
  PsInitializeSystemPageTable();
  UserModeEntry();
  return 0;
}

void UserModeEntry(void) {
  struct {
    WORD wUnused;
    WORD wLimit;
    DWORD dwBase;
  } GDTPtr;
  __SGDT(&GDTPtr.wLimit);
  RtlCopyMemory(GDT, (LPVOID)GDTPtr.dwBase, GDTPtr.wLimit + 1);
  GDTPtr.dwBase = (DWORD)(DWORD_PTR)GDT;
  __LGDT(&GDTPtr.wLimit);
  DevHardDiskRead(1, 0, 3, bFirstProc);
  register HANDLE hProcess = PsStartProcess(bFirstProc);
  __set_cr3(((LPPROCESS_CONTEXT)hProcess)->cr3);
  static char s[0x100];
  RtlSPrintf(s, "%x\n", hProcess);
  __SYSCALL1(3, s);
  __SYSCALL1(3, 0x401000);
  __SYSCALL1(3, 0x402000);
  __STI();
  while (1)
    __HALT();
}

void Panic(const LPSTR lpPanicMsg) {
  __SYSCALL0(0);
  __SYSCALL1(2, 0);
  __SYSCALL1(3, lpPanicMsg);
  __asm__("cli");
  __asm__("hlt");
}
